<template>
  <!-- tinymce-script-src使用public中的static/tinymce -->
  <TinymceEditor v-model="contentValue" :init="initOption" tinymce-script-src="/static/tinymce/tinymce.min.js" />
  <FileSelect v-model:show="showSelectImg" :file-tp-cd="fileTpCd" :select-count="10" @selected="handleSelectedImg" />
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';
import TinymceEditor from '@tinymce/tinymce-vue';
import type { Editor, RawEditorOptions } from './tinymce';
import type { ComponentPropType } from '@/types/utils';
import { uploadFile001, uploadFile005 } from '@/modules/bmpfl/api/file-upload';
import { FileSelect } from '@/components/SelectMediaWrap';

type IFileSelectOnSelectedParam = Parameters<ComponentPropType<typeof FileSelect, 'onSelected'>>[0];

const props = withDefaults(
  defineProps<{
    value: string;
    fileTpCd: string;
    height?: number | string;
    loading: boolean;
  }>(),
  {
    value: '',
    fileTpCd: '',
    height: 800,
    loading: false
  }
);

const emit = defineEmits<{
  (e: 'update:value', value: string): void;
  (e: 'update:loading', value: boolean): void;
}>();

let tEditor: Editor | null = null;
const showSelectImg = ref<boolean>(false);

const contentValue = computed({
  get() {
    return props.value;
  },
  set(value) {
    emit('update:value', value);
  }
});

const initOption: RawEditorOptions = {
  width: '100%',
  height: props.height,
  language: 'zh-Hans',
  plugins: 'code table',
  toolbar: 'undo redo | styles | bold italic | alignleft alignright aligncenter alignjustify alignnone | table code selectImages',
  promotion: false, //高级升级促销选项
  branding: false,
  // statusbar: false,
  images_upload_handler: (blobInfo) => {
    //本地粘贴或拖拽进入编辑框的图片上传处理
    const loadingDom = upLoading();
    emit('update:loading', true);
    tEditor?.getContainer().appendChild(loadingDom);
    return tinymceImgUpHandle(blobInfo.blob() as File).finally(() => {
      loadingDom.remove();
      emit('update:loading', false);
    });
  },
  setup: (editor: Editor) => {
    tEditor = editor;
    tEditor.ui.registry.addButton('selectImages', {
      icon: 'gallery',
      tooltip: '选择图片',
      onAction: () => {
        showSelectImg.value = true;
      }
    });
    tEditor.on('SetContent', (e) => {
      if (!e.paste) return;
      const bodyDom = tEditor?.getBody();
      if (!bodyDom) return;
      //对图片进行判断并进行上传处理，本地粘贴或已在对象存储中的图片不处理，只处理远程图片
      const imgDoms = bodyDom.querySelectorAll<HTMLImageElement>('img');
      const whiteHost = ['http://kodo.yanfan.ltd', 'https://kodo.yanfan.ltd', 'http://kodo.wowxiangfu.com', 'https://kodo.wowxiangfu.com', location.origin]; //图片src为此来源时不用处理
      const imgDomList: HTMLImageElement[] = [];
      for (const imgDom of imgDoms) {
        try {
          const urlObj = new URL(imgDom.src);
          if (!whiteHost.includes(urlObj.origin)) {
            imgDomList.push(imgDom);
          }
        } catch (e) {
          imgDom.remove();
        }
      }
      if (imgDomList.length < 1) return;
      const loadingDom = upLoading();
      emit('update:loading', true);
      tEditor?.getContainer().appendChild(loadingDom);
      upRemotePasteImages(imgDomList).finally(() => {
        loadingDom.remove();
        emit('update:loading', false);
      });
    });
  }
};
//从文件中心选择图片后处理
function handleSelectedImg(data: IFileSelectOnSelectedParam): void {
  const appendStr = data.checkedFileUrlList.map((item) => `<img src="${item}"/>`).join('');
  tEditor?.execCommand('mceInsertContent', false, appendStr);
}
//本地粘贴或拖拽进入编辑框的图片上传处理
function tinymceImgUpHandle(file: File): Promise<string> {
  return new Promise<File>((resolve) => {
    if (!['image/jpeg', 'image/png'].includes(file.type)) {
      resolve(file);
      return;
    }
    const objUrl = URL.createObjectURL(file);
    const img = new Image();
    img.addEventListener('load', () => {
      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;
      const ctx = canvas.getContext('2d');
      if (!ctx) {
        resolve(file);
        return;
      }
      ctx.drawImage(img, 0, 0);
      const webpDataUrl = canvas.toDataURL('image/webp');
      if (webpDataUrl.startsWith('data:image/webp')) {
        const tFile = dataURLtoFile(webpDataUrl, file.name.replace(/\.[0-9a-z]+$/, '.webp'));
        resolve(tFile);
      } else {
        resolve(file);
      }
    });
    img.src = objUrl;
  }).then((tFile) => {
    return uploadFile001({ fileTpCd: props.fileTpCd }, tFile)
      .then((res) => {
        return Promise.resolve(res.fileUrl);
      })
      .catch(() => {
        return Promise.reject('上传失败');
      });
  });
}

//base64转File
function dataURLtoFile(dataurl: string, filename: string): File {
  let arr = dataurl.split(','),
    mime = ((arr[0] ?? '').match(/:(.*?);/) ?? [])[1] ?? '',
    bstr = atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, { type: mime });
}
//上传图片时加载样式
function upLoading(): HTMLDivElement {
  const maskDom = document.createElement('div');
  maskDom.style.position = 'absolute';
  maskDom.style.top = '0';
  maskDom.style.right = '0';
  maskDom.style.bottom = '0';
  maskDom.style.left = '0';
  maskDom.style.display = 'flex';
  maskDom.style.backgroundColor = 'rgba(0,0,0,.1)';
  //loading svg中的样式依赖于 naive-ui <style cssr-id="n-base-loading"></style>中的样式
  const loadingSvg =
    '<svg class="n-base-loading__icon" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"><g><animateTransform attributeName="transform" type="rotate" values="0 100 100;270 100 100" begin="0s" dur="1.6s" fill="freeze" repeatCount="indefinite"></animateTransform><circle class="n-base-loading__icon" fill="none" stroke="currentColor" stroke-width="16" stroke-linecap="round" cx="100" cy="100" r="92" stroke-dasharray="567" stroke-dashoffset="1848"><animateTransform attributeName="transform" type="rotate" values="0 100 100;135 100 100;450 100 100" begin="0s" dur="1.6s" fill="freeze" repeatCount="indefinite"></animateTransform><animate attributeName="stroke-dashoffset" values="567;142;567" begin="0s" dur="1.6s" fill="freeze" repeatCount="indefinite"></animate></circle></g></svg>';
  const svgDivDom = document.createElement('div');
  svgDivDom.style.margin = 'auto';
  svgDivDom.style.width = '60px';
  svgDivDom.style.height = '60px';
  svgDivDom.style.color = '#2080f0';
  svgDivDom.style.animation = 'rotator 3s linear infinite both';
  svgDivDom.innerHTML = loadingSvg;
  maskDom.appendChild(svgDivDom);
  return maskDom;
}
/**
 * 处理粘贴，拖拽方式进入的图片，进行上传处理
 * 判断src是否为本地地址，本地地址则使用前端上传，远程地址则提交地址信息至后端处理（远程图片地址有跨域问题canvas可能无法处理图片）
 * @param imgDomList
 */
// function handlePasteImage(imgDomList: HTMLImageElement[]) {
//   const upPromises: Promise<void>[] = [];
//   const upRemoteImgList: HTMLImageElement[] = [];
//   for (const imgDom of imgDomList) {
//     const urlObj = new URL(imgDom.src);
//     if (urlObj.origin === location.origin) {
//       upPromises.push(upLocalPasteImage(imgDom));
//     } else {
//       upRemoteImgList.push(imgDom);
//     }
//   }
//   if (upRemoteImgList.length > 0) {
//     upPromises.push(upRemotePasteImages(upRemoteImgList));
//   }
//   return Promise.all(upPromises);
// }
//多张远程图片的请求后台上传出来，上传图片成功后修改原图片dom的src及data-mce-src属性
function upRemotePasteImages(imgDomList: HTMLImageElement[]) {
  if (imgDomList.length < 1) return Promise.resolve();
  const postData: { fileId: string; fileUrl: string }[] = [];
  for (let i = 0; i < imgDomList.length; i++) {
    postData.push({ fileId: String(i), fileUrl: imgDomList[i].src });
  }
  return uploadFile005({ fileTpCd: props.fileTpCd, fileUrlList: postData }).then((res) => {
    const fileIdList = res.fileUrlList;
    for (let i = 0; i < imgDomList.length; i++) {
      const currFile = fileIdList.find((item) => item.fileId === String(i));
      if (currFile && currFile.fileUrl) {
        imgDomList[i].src = currFile.fileUrl;
        imgDomList[i].setAttribute('data-mce-src', currFile.fileUrl);
      } else {
        window.$message.error(`第${i + 1}张，${currFile?.errorMsg || '未知错误'}，已移除`);
        imgDomList[i].remove();
      }
    }
  });
}
//单张本地图片的上传处理，上传图片成功后修改原图片dom的src及data-mce-src属性
// function upLocalPasteImage(imgDom: HTMLImageElement): Promise<void> {
//   return new Promise((resolve, reject) => {
//     const newImg = new Image();
//     newImg.crossOrigin = 'anonymous';
//     newImg.onload = () => {
//       const canvas = document.createElement('canvas');
//       const ctx = canvas.getContext('2d');
//       canvas.width = newImg.naturalWidth;
//       canvas.height = newImg.naturalHeight;
//       if (!ctx) {
//         imgDom.remove();
//         window.$message.error('图片处理失败');
//         reject();
//         return;
//       }
//       ctx.drawImage(newImg, 0, 0);
//       canvas.toBlob((blob) => {
//         if (!blob) {
//           imgDom.remove();
//           window.$message.error('图片处理失败');
//           reject();
//           return;
//         }
//         const fileName = Date.now() + String(Math.floor(Math.random() * 10000)).padStart(4, '0');
//         const file = new File([blob], `${fileName}.webp`);
//         uploadFile001({ fileTpCd: props.fileTpCd }, file)
//           .then((res) => {
//             if (res.status === 200) {
//               if (res.data.errcode == '0') {
//                 return Promise.resolve(res.data.fileUrl);
//               } else {
//                 return Promise.reject(res.data.errmsg);
//               }
//             } else {
//               return Promise.reject(res.statusText);
//             }
//           })
//           .then((res) => {
//             imgDom.src = res;
//             imgDom.setAttribute('data-mce-src', res);
//             resolve();
//           })
//           .catch((err) => {
//             imgDom.remove();
//             window.$message.error(`上传失败,${err}`);
//             reject();
//           });
//       }, 'image/webp');
//     };
//     newImg.onerror = () => {
//       imgDom.remove();
//       window.$message.error('图片加载失败');
//       reject();
//     };
//     newImg.src = imgDom.src;
//   });
// }
</script>

<style lang="scss" scoped></style>
